package com.cobo.zettelkasten.core

import cn.hutool.core.lang.Snowflake
import cn.hutool.core.util.IdUtil
import org.apache.commons.io.FileUtils
import org.apache.commons.lang3.StringUtils
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.HashSet

interface MarkdownProcessor {
    fun process(richText: RichText): Markdown
}

/**
 * tags,categories 需要用来构建分类云，需要单独处理，并且以后增加自动分类，和标签，
 * 提取出来单独处理
 * */
data class Markdown(
    val richText: RichText,
    var markdown: String,
    // 标签
    val tags: Set<String>,
    // 分类
    val categories: Set<String>,
    val doubleLink: Set<String> = HashSet<String>(),
    val filepath: String,
    var globalId: String = "",
) {
    val formatter: HashMap<String, String> get() = richText.formatter
    val content: String get() = richText.content
}

/**
 * @param filepath 当前扫描文件路径
 * @param basedir 项目路径
 * */
data class Context(val filepath: String, val basedir: String) {

    /**
     * 计算相对路径，使用字符串截断会不会好一点，但是得做系统测试
     * */
    fun calcParentDir(matchDir: String, searchDir: String): Stack<String> {
        val parentFile = File(searchDir).parentFile ?: return Stack()


        if (parentFile.absolutePath.equals(File(matchDir).absolutePath)) {
            val parentStack = Stack<String>()
            parentStack.push(parentFile.name)
            return parentStack
        }

        val parentStack = Stack<String>()
        parentStack.push(parentFile.name)
        parentStack.addAll(calcParentDir(matchDir, parentFile.absolutePath))
        return parentStack
    }
}

class CommonMarkdown(val ctx: Context) : MarkdownProcessor {


    override fun process(richText: RichText): Markdown {

        val tags = richText.formatter["tag"]?.split(",")?.toSet() ?: HashSet<String>()
        val categories = richText.formatter["category"]?.split(",")?.toSet() ?: HashSet<String>()

        return Markdown(richText, richText.content, tags, categories, filepath = ctx.filepath)

    }
}


abstract class MarkdownDecorator(open val processor: MarkdownProcessor, open val ctx: Context) : MarkdownProcessor {

    override fun process(richText: RichText): Markdown {
        return doProcess(processor.process(richText))
    }

    abstract fun doProcess(processMarkdown: Markdown): Markdown
}

/**
 * 本地图片直接写到文章里面,方便渲染
 * */
class LocalImageBase64EmbedDecorator(override val processor: MarkdownProcessor, override val ctx: Context) :
    MarkdownDecorator(processor, ctx) {

    val imgLinkRegx = "\\!\\[.*?\\]\\((.*?)\\)".toRegex()

    fun imageToBase64(imagePath: String, imageType: String = "image/png"): String {
        // 读取图片文件为字节数组
        val file = File(imagePath)
        val bytes = file.readBytes()

        // 使用Base64编码字节数组
        val encoder = Base64.getEncoder()
        val encoded = encoder.encodeToString(bytes)

        // 返回带有MIME类型的Base64字符串
        return "data:$imageType;base64,$encoded"
    }

    /**
     * 本地图片直接写到文章里面,方便渲染
     * */
    override fun doProcess(processMarkdown: Markdown): Markdown {
        //替换markdown的图片地址
        val imgMatch = imgLinkRegx.findAll(processMarkdown.markdown).map { it.groups[1]!!.value }
        for (imgPath in imgMatch) {
            //使用的都是相对路径，所以这处拿markdown的相对路径
            //todo 使用的是互联网图片需要单独处理
            if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) {
                continue
            }
            val file = File(File(ctx.filepath).parentFile.absolutePath, imgPath)
            /**
             * 1. svg 直接读取xml即可
             * 2. 其他图片，转base64
             * */
            if (file.exists()) {
                //svg 需要单独处理
                if (imgPath.endsWith("svg")) {
                    val svgXml = FileUtils.readFileToString(file, "utf-8")
                    processMarkdown.markdown = StringUtils.replace(processMarkdown.markdown, "($imgPath)", svgXml)

                } else {
                    val imgBase64 = imageToBase64(file.absolutePath)
                    processMarkdown.markdown = StringUtils.replace(processMarkdown.markdown, imgPath, imgBase64)
                }
            }
        }
        return processMarkdown
    }
}

class TitleFormatterDecorator(override val processor: MarkdownProcessor, override val ctx: Context) :
    MarkdownDecorator(processor, ctx) {

    /**
     * 解析文章标题
     * @param content 文章内容
     * @return Pair<标题, 正文内容>
     */
    fun extractFirstTitle(content: String): Pair<String, String> {
        // 按行分割字符串
        val lines = content.lines()
        return if (lines.isEmpty()) {
            // 如果字符串为空，返回两个空字符串
            "" to ""
        } else {
            // 提取第一行
            val firstLine = lines.first()
            if (firstLine.startsWith("# ")) {
                // 拼接剩余行
                val remainingLines = lines.drop(1).joinToString(separator = "\n")
                firstLine.substringAfter("# ") to remainingLines
            } else {
                "" to content
            }
        }
    }

    override fun doProcess(processMarkdown: Markdown): Markdown {
        /**
         * formatter不包含 title的时候，读取文章首个一级标题或者文件名为标题
         * 1. # **** 首行为标准大标题，则为标题
         * 2. 其他取文件名
         * */
        if (processMarkdown.formatter["title"] == null) {
            val contentPair = extractFirstTitle(processMarkdown.markdown)

            processMarkdown.formatter["title"] = if (contentPair.first != "") {
                contentPair.first
            } else {
                File(ctx.filepath).nameWithoutExtension
            }
            // 文章选去掉首标题的
            processMarkdown.markdown = contentPair.second
        }
        return processMarkdown
    }
}

/**
 * 计算父目录加入tags
 * todo 找到代码里所有得 #xxx 设置为标签, 后续加入nlp 文章主题自动计算
 * */
class TagsFormatterDecorator(override val processor: MarkdownProcessor, override val ctx: Context) :
    MarkdownDecorator(processor, ctx) {
        // 匹配 #标签语法
    val tagRegex = "#(\\S+)".toRegex()

    override fun doProcess(processMarkdown: Markdown): Markdown {
        val parentDir = ctx.calcParentDir(ctx.basedir, ctx.filepath)
        (processMarkdown.tags as MutableSet<String>).addAll(parentDir)
        // 标签按长度从长到短排序 避免错误的替换
        val tagMatch = tagRegex.findAll(processMarkdown.markdown).map { it.groups[1]!!.value }.sortedByDescending { it.length }
        for (tag in tagMatch) {
            processMarkdown.markdown =
                StringUtils.replace(processMarkdown.markdown, "#$tag", "")
        }
        processMarkdown.tags.addAll(tagMatch)
        return processMarkdown
    }
}

/**
 * 计算父目录加入categories
 * */
class CategoryFormatterDecorator(override val processor: MarkdownProcessor, override val ctx: Context) :
    MarkdownDecorator(processor, ctx) {

    override fun doProcess(processMarkdown: Markdown): Markdown {
        val parentDir = ctx.calcParentDir(ctx.basedir, ctx.filepath)
        (processMarkdown.categories as MutableSet<String>).addAll(parentDir)
        return processMarkdown
    }
}

/**
 * 增加用户名 创建时间 最后修改时间
 * */
class BaseInfoFormatterDecorator(
    username: String,
    override val processor: MarkdownProcessor,
    override val ctx: Context
) : MarkdownDecorator(processor, ctx) {

    private val authorName = username
    private val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.s")

    override fun doProcess(processMarkdown: Markdown): Markdown {
        val lastModified = File(ctx.filepath).lastModified()
        val lastModifiedDate = Date(lastModified)
        val modifyTime = dateFormatter.format(lastModifiedDate)
        processMarkdown.formatter["createUser"] = processMarkdown.formatter["createUser"] ?: authorName
        processMarkdown.formatter["createTime"] = processMarkdown.formatter["createTime"] ?: modifyTime
        processMarkdown.formatter["lastModifyTime"] = processMarkdown.formatter["lastModifyTime"] ?: modifyTime
        return processMarkdown
    }

}

/**
 * 文本双向链接提取[[]]
 * */
class DoubleLinkDecorator(override val processor: MarkdownProcessor, override val ctx: Context) :
    MarkdownDecorator(processor, ctx) {
    val doubleLinkRegx = "\\[\\[([^\\[\\]]*)\\]\\]".toRegex()

    override fun doProcess(processMarkdown: Markdown): Markdown {
        val matches = doubleLinkRegx.findAll(processMarkdown.markdown).map { it.groups[1]!!.value }
        // 双向链接直接通过原文件名无后缀绑定，
        (processMarkdown.doubleLink as MutableSet<String>).addAll(matches)
        return processMarkdown
    }
}

class MarkdownAdvanceFactory(val ctx: Context) {
    fun commonMarkdown(): MarkdownProcessor = CommonMarkdown(ctx)

    fun doubleLinkDecorator(): (MarkdownProcessor) -> MarkdownProcessor = { processor ->
        DoubleLinkDecorator(processor, ctx)
    }

    fun baseInfoFormatterDecorator(author:String): (MarkdownProcessor) -> MarkdownProcessor = { processor ->
        BaseInfoFormatterDecorator(author, processor, ctx)
    }

    fun categoryFormatterDecorator(): (MarkdownProcessor) -> MarkdownProcessor = { processor ->
        CategoryFormatterDecorator(processor, ctx)
    }

    fun tagsFormatterDecorator(): (MarkdownProcessor) -> MarkdownProcessor = { processor ->
        TagsFormatterDecorator(processor, ctx)
    }

    fun titleFormatterDecorator(): (MarkdownProcessor) -> MarkdownProcessor = { processor ->
        TitleFormatterDecorator(processor, ctx)
    }

    fun localImageBase64EmbedDecorator(): (MarkdownProcessor) -> MarkdownProcessor = { processor ->
        LocalImageBase64EmbedDecorator(processor, ctx)
    }
}


// 链式调用扩展函数
infix fun MarkdownProcessor.decorateWith(decorator: (MarkdownProcessor) -> MarkdownProcessor): MarkdownProcessor =
    decorator(this)

/**
 * 雪花算法ID生成器
 */
class SnowFlakeIdGenerator {

    val snowflake: Snowflake = IdUtil.getSnowflake(1, 1)

    fun nextSnowFlakeId(): String {
        return "${snowflake.nextId()}"
    }
}